package de.karlNet.sambamanager.ldapController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.naming.directory.SearchControls;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;

import de.karlNet.sambamanager.ldapModel.LdapObject;

public abstract class AbstractLdapTree implements ILdapTree {
	protected abstract LdapTemplate getLdapTemplate();

	@Value("${ldap.baseDN}")
	public String baseDN;

	public String getBaseDN() {
		return baseDN;
	}

	@Autowired
	protected AttributesMapper attributesMapper;

	private HashMap<String, List<LdapObject>> cachedTree;
	private HashMap<String, LdapObject> cachedObjects;

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public List<LdapObject> searchFor(String searchTerm) {
		List ldapObjects = getLdapTemplate().search(
				this.getBaseDN(),
				"(|(name=" + searchTerm + ")(dn=" + searchTerm + "*)(cn="
						+ searchTerm + "*)(samaccountname=" + searchTerm
						+ "*))", SearchControls.SUBTREE_SCOPE,
				this.attributesMapper);
		List<LdapObject> ret = new ArrayList<LdapObject>(ldapObjects);
		return ret;
	}

	@Override
	public List<LdapObject> getAllChildrenOfDN(LdapObject ldapObject) {
		return this.getAllChildrenOfDN(ldapObject.getDn());
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public List<LdapObject> getAllChildrenOfDN(String dn) {
		List ldapObjects = getLdapTemplate().search(dn, this.getObjectClass(),
				SearchControls.SUBTREE_SCOPE, this.attributesMapper);
		List<LdapObject> ret = new ArrayList<LdapObject>(ldapObjects);
		return ret;
	}

	@Override
	public List<LdapObject> getChildrenOfDN(String dn) {
		return this.getChildrenOfDNBulked(dn);
	}

	public List<LdapObject> getChildrenOfDNBulked(String dn) {
		if (this.cachedTree == null) {
			this.cachedTree = new HashMap<String, List<LdapObject>>();
			this.initCache();
		}
		List<LdapObject> ldapObjects = this.cachedTree.get(dn.toLowerCase());
		if (ldapObjects == null) {
			return new ArrayList<LdapObject>();
		}
		return ldapObjects;
	}

	@Override
	public void reset() {
		this.initCache();
	}

	@PostConstruct
	private void initCache() {

		this.cachedTree = new HashMap<String, List<LdapObject>>();
		this.cachedObjects = new HashMap<String, LdapObject>();
		for (LdapObject ldapObject : this.getAllChildrenOfDN(this.getBaseDN())) {
			
				this.cachedObjects.put(ldapObject.getDn().toLowerCase(),
						ldapObject);
				DistinguishedName distinguishedName=null;
				try {
				 distinguishedName = new DistinguishedName(
						ldapObject.getDn());
				} catch (Exception e) {
					e.printStackTrace();
				}
				String rdn = distinguishedName
						.getPrefix(distinguishedName.size() - 1).toString()
						.toLowerCase();
				if (this.cachedTree.get(rdn) == null) {
					this.cachedTree.put(rdn, new ArrayList<LdapObject>());
				}
				this.cachedTree.get(rdn).add(ldapObject);
			
		}

	}

	@Override
	public LdapObject getObject(String dn) {
		if (this.cachedObjects.get(dn.toLowerCase()) == null) {
			System.out.println("null detected");
		}
		return this.cachedObjects.get(dn.toLowerCase());
	}

	protected String getObjectClass() {
		return "(objectClass=*)";
	}
}